問題解説: Docker 実技2
問題文
Kubernetes クラスタ環境を移行後、Kubernetes上のWordPressにアクセスできなくなりました。
原因をつきとめ、修正してください。
情報
Kubernetesクラスタの移行手順書は存在しません。アドレスレンジに以下の変更があったことのみ分かっています。
Address Range: 172.16.0.0/24
-> 192.168.0.0/24
Cluster Address Range: 192.168.0.0/24
-> 10.254.0.0/24
- Address Range: Kubernetes Node に振られるアドレスのレンジ
- Cluster Address Range: Kubernetes Service に振られるアドレスのレンジ
ゴール
VNC 踏み台サーバ上のブラウザにて「192.168.0.1:30080」を入力することで、WordPressの画面が表示される
トラブルの概要
Kubernetes Pod の名前解決に失敗するため、wordpress Pod のセットアップに失敗する。その結果、外部からWordPressへのhttpアクセスに失敗する。
解説
当問題の環境では、kube-apiserver が掴んでいる証明書の Subject Alternative Names (SANs) が誤っていることが原因で、Pod からsvc/kubernetesへhttpsでの通信が失敗します。そのため svc/kubernetes との通信が必要である kube-dns Pod を正常に動作させることが出来ず、wordpress Pod から mysql.default.svc.cluster.local:3306
への通信における名前解決に失敗するため、wordpress Pod のセットアップに失敗するという問題が生じます。
当問題は、kube-apiserver が正常な SANs を持つ証明書を掴むよう、証明書を再生成することで解決します。
なおKubernetesは、kube-apiserverの --tls-cert-file
オプションにサーバ証明書、 --tls-private-key-file
オプションに秘密鍵、kube-controller-managerの --root-ca-file
オプションにCA証明書を指定することで Podからsvc/kubernetesへのhttps通信が可能となります。
回答例
まず、現在のPodの稼働状況を確認します。
# kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-755f57f594-mc9cr 1/1 Running 1 2h
wordpress-dc9bb949d-9glcx 1/1 Running 4 2h
wordpress-dc9bb949d-sntj9 1/1 Running 4 2h
アクセス先のPodである pod/wordpress のログを確認します。
# kubectl logs wordpress-dc9bb949d-9glcx
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
Warning: mysqli::__construct(): php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution in Standard input code on line 22
Warning: mysqli::__construct(): (HY000/2002): php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution in Standard input code on line 22
MySQL Connection Error: (2002) php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution
... (省略)
以上のログより名前解決に失敗していることがわかります。/root/manifests/wordpress/deployment-wordpress.yaml
の中身を見ると、WORDPRESS_DB_HOST
環境変数に mysql.default.svc.cluster.local
というドメイン名が渡されているため、当箇所における名前解決に失敗しているものであると考えられます。
Kubernetesクラスタ内のServiceリソースの名前解決は kube-system
ネームスペース内の kube-dns Pod が行います。
kube-dns Pod の稼働状況を確認します。
# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
kube-dns-7c7877989-8frxc 2/3 Running 1 1m
kube-dns Pod のログを確認します。
# kubectl -n kube-system logs kube-dns-7c7877989-8frxc kubedns
...(省略)
E0729 13:29:49.132142 1 reflector.go:201] k8s.io/dns/pkg/dns/dns.go:192: Failed to list *v1.Service: Get https://10.254.0.1:443/api/v1/services?resourceVersion=0: x509: certificate is valid for 172.16.0.1, 192.168.0.1, not 10.254.0.1
E0729 13:29:49.139524 1 reflector.go:201] k8s.io/dns/pkg/dns/dns.go:189: Failed to list *v1.Endpoints: Get https://10.254.0.1:443/api/v1/endpoints?resourceVersion=0: x509: certificate is valid for 172.16.0.1, 192.168.0.1, not 10.254.0.1
...(省略)
以上のエラーログより、kube-apiserverの持つ証明書のSANsに10.254.0.1が存在しないことがわかります。
サーバ証明書に対応する秘密鍵である /etc/pki/tls/kube01/server.key
を用いてCSRファイルを再生成します。以下はopensslを用いた例です。
# cat << 'EOF' > /etc/pki/tls/kube01/csr.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
C = JP
ST = dummy
L = dummy
O = dummy
OU = dummy
CN = dummy
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster
DNS.5 = kubernetes.default.svc.cluster.local
DNS.6 = kube01
IP.1 = 192.168.0.1
IP.2 = 10.254.0.1
[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names
EOF
# openssl req -new -key server.key -out server.csr -config csr.conf
CSRファイルに対し、CA証明書である/etc/pki/tls/kube01/ca.crt
にて署名を行うことでCRTファイルを生成します。
# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 10000 \
-extensions v3_ext -extfile csr.conf
kube-apiserver.service を再起動します。
# systemctl restart kube-apiserver.service
Pod を明示的に再起動させるため、Pod を再作成します。
# kubectl delete -f /root/manifests/kube-dns/kube-dns.yml &amp;amp;&amp;amp; kubectl create -f /root/manifests/kube-dns/kube-dns.yml
# kubectl delete -f /root/manifests/wordpress/wordpress.yaml &amp;amp;&amp;amp; kubectl create -f /root/manifests/wordpress/wordpress.yaml
講評
Kubernetesは利用者が簡単にコンテナのオーケストレーションを行える反面、内部の仕組みは複雑なものになっています。そのためKubernetesについて学ぶ際は、GCP などのクラウドサービスにてKuberntesを利用する他、Kubernetesを実際に構築してみることに是非チャレンジすると良いと思います!